package excel

import (
	"context"
	"data-system/config"
	"data-system/excel/wps"
	"data-system/models"
	"data-system/tools"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime/debug"
	"strings"
	"sync"
	"syscall"
	"time"
	"unsafe"

	"github.com/fsnotify/fsnotify"
	"github.com/gorilla/websocket"
	"github.com/tealeg/xlsx"
	"github.com/xuri/excelize/v2"
	"github.com/zzl/go-com/com"
	"github.com/zzl/go-com/ole"
	"github.com/zzl/go-win32api/v2/win32"
)

var cellRangeIndex = map[int]string{
	1: "A", 2: "B", 3: "C", 4: "D", 5: "E", 6: "F", 7: "G", 8: "H", 9: "I", 10: "J", 11: "K", 12: "L", 13: "M", 14: "N",
	15: "O", 16: "P", 17: "Q", 18: "R", 19: "S", 20: "T", 21: "U", 22: "V", 23: "W", 24: "X", 25: "Y", 26: "Z",

	27: "AA", 28: "AB", 29: "AC", 30: "AD", 31: "AE", 32: "AF", 33: "AG", 34: "AH", 35: "AI", 36: "AJ", 37: "AK", 38: "AL", 39: "AM",
	40: "AN", 41: "AO", 42: "AP", 43: "AQ", 44: "AR", 45: "AS", 46: "AT", 47: "AU", 48: "AV", 49: "AW", 50: "AX", 51: "AY", 52: "AZ",

	53: "BA", 54: "BB", 55: "BC", 56: "BD", 57: "BE", 58: "BF", 59: "BG", 60: "BH", 61: "BI", 62: "BJ", 63: "BK", 64: "BL", 65: "BM",
	66: "BN", 67: "BO", 68: "BP", 69: "BQ", 70: "BR", 71: "BS", 72: "BT", 73: "BU", 74: "BV", 75: "BW", 76: "BX", 77: "BY", 78: "BZ",

	79: "CA", 80: "CB", 81: "CC", 82: "CD", 83: "CE", 84: "CF", 85: "CG", 86: "CH", 87: "CI", 88: "CJ", 89: "CK", 90: "CL", 91: "CM",
	92: "CN", 93: "CO", 94: "CP", 95: "CQ", 96: "CR", 97: "CS", 98: "CT", 99: "CU", 100: "CV", 101: "CW", 102: "CX", 103: "CY", 104: "CZ",
}

var Clients = make(map[string]*websocket.Conn)

// ExcelChange 表示 Excel 文件变更信息
type ExcelChange struct {
	FilePath       string
	FileName       string
	Op             string
	ChangedRows    []int
	ChangedRowData [][]string
	ChangeTime     time.Time
}

// ExcelWatcher 监听 Excel 文件变化
type ExcelWatcher struct {
	watcher      *fsnotify.Watcher
	paths        []string
	fileStates   map[string]ExcelFileState // 文件状态缓存
	debounceMap  map[string]*time.Timer    // 防抖计时器
	debounceTime time.Duration
	changeChan   chan ExcelChange // 变更通知通道
	mu           sync.Mutex
}

// ExcelFileState 存储 Excel 文件状态
type ExcelFileState struct {
	LastModified time.Time
	Sheets       map[string][][]string // 工作表名:行数据
}

func ReadFile(fileUrl string, dataLine int) ([]models.ExcelData, error) {
	f, err := excelize.OpenFile(fileUrl)
	if err != nil {
		return nil, fmt.Errorf("打开文件失败: %w", err)
	}
	defer f.Close()
	// 获取第一个工作表
	sheetName := f.GetSheetName(0)
	if sheetName == "" {
		return nil, fmt.Errorf("未找到工作表")
	}
	// 读取所有行数据
	rows, err := f.GetRows(sheetName)
	if err != nil {
		return nil, fmt.Errorf("读取行数据失败: %w", err)
	}
	if len(rows) < 2 {
		return nil, fmt.Errorf("文件数据不足（至少需要表头+1行数据）")
	}
	// 提取表头（第一行）
	headers := make([]string, len(rows[0]))
	for j, col := range rows[0] {
		headers[j] = strings.TrimSpace(col)
	}
	// 从第二行开始构建数据
	var excelDataRows = make([]models.ExcelData, 0)
	for i := dataLine; i < len(rows); i++ {
		row := make(map[string]string)
		originRow := rows[i]
		for j, cellValue := range originRow {
			if j >= len(headers) {
				break
			}
			key := headers[j]
			value := strings.TrimSpace(cellValue)
			row[key] = value
		}
		excelDataRows = append(excelDataRows, models.ExcelData{OriginRow: originRow, Row: row})
	}
	return excelDataRows, nil
}

func ReadFileContent(fileUrl string) ([][]string, error) {
	f, err := excelize.OpenFile(fileUrl)
	if err != nil {
		return nil, fmt.Errorf("打开文件失败: %w", err)
	}
	defer f.Close()
	// 获取第一个工作表
	sheetName := f.GetSheetName(0)
	if sheetName == "" {
		return nil, fmt.Errorf("未找到工作表")
	}
	// 读取所有行数据
	rows, err := f.GetRows(sheetName)
	if err != nil {
		return nil, fmt.Errorf("读取行数据失败: %w", err)
	}
	return rows, nil
}

// IsFileLocked 检查文件是否被其他进程锁定（可能正在使用）
func IsFileLocked(fileUri string) (bool, error) {
	if !tools.IsFileExists(fileUri) {
		return false, nil
	}
	file, err := os.OpenFile(fileUri, os.O_RDWR, 0666)
	if err != nil {
		// 检查错误类型是否为权限被拒绝或文件正在使用中
		if perr, ok := err.(*os.PathError); ok {
			if errno, ok := perr.Err.(syscall.Errno); ok {
				// Windows错误代码: ERROR_SHARING_VIOLATION (32) 表示文件正被其他进程使用
				// ERROR_LOCK_VIOLATION (33) 表示文件的一部分被锁定
				if errno == 32 || errno == 33 {
					return true, nil
				}
			}
		}
		return false, err // 其他错误
	}
	defer file.Close()
	return false, nil // 成功打开，文件未被锁定
}

func WriteConfigData(nodeType string, rows [][]string) error {
	fileName := nodeType + config.Cfg().FileSuffix
	fullUri := config.Cfg().ConfigDir + "/" + nodeType + config.Cfg().FileSuffix
	isOpen, err := IsFileLocked(fullUri)
	if err != nil {
		log.Fatal("正常文件检测是否打开异常", err)
		return err
	}
	if isOpen {
		log.Println("-----【正常数据】打开文件写数据：fileUri=" + fullUri)
		return WriteWps(fileName, fullUri, rows)
	} else {
		log.Println("-----【正常数据】未打开文件写数据：fileUri=" + fullUri)
		return Write(fullUri, rows)
	}
}

func WriteConflictData(nodeType string, rows [][]string) error {
	fileName := nodeType + config.Cfg().FileConflictSuffix
	fullUri := config.Cfg().ConflictDir + "/" + nodeType + config.Cfg().FileConflictSuffix
	isOpen, err := IsFileLocked(fullUri)
	if err != nil {
		log.Fatal("冲突文件检测是否打开异常", err)
		return err
	}
	if isOpen {
		log.Println("-----【冲突数据】打开文件写数据：fileUri=" + fullUri)
		return WriteWps(fileName, fullUri, rows)
	} else {
		log.Println("-----【冲突数据】未打开文件写数据：fileUri=" + fullUri)
		return Write(fullUri, rows)
	}
}

func Write(fullUri string, data [][]string) error {
	f := excelize.NewFile()
	defer f.Close()
	// 创建新工作表
	f.NewSheet("sheet1")
	// 使用流式写入器提高性能
	streamWriter, err := f.NewStreamWriter("sheet1")
	if err != nil {
		return fmt.Errorf("创建流式写入器失败: %v", err)
	}
	// 流式写入数据
	for rowIdx, row := range data {
		// 将字符串切片转换为interface{}切片[5](@ref)
		rowData := make([]interface{}, len(row))
		for i, v := range row {
			rowData[i] = v
		}
		cellStart, _ := excelize.CoordinatesToCellName(1, rowIdx+1)
		streamWriter.SetRow(cellStart, rowData)
	}
	// 刷新流
	if err := streamWriter.Flush(); err != nil {
		return fmt.Errorf("流刷新失败: %v", err)
	}
	// 删除已存在的文件
	if _, err := os.Stat(fullUri); err == nil {
		os.Remove(fullUri)
	}
	return f.SaveAs(fullUri)
}

func WriteWps(fileName, fullUri string, rows [][]string) error {
	if !tools.IsFileExists(fullUri) {
		file := xlsx.NewFile()
		file.AddSheet("Sheet1")
		err := file.Save(fullUri)
		if err != nil {
			log.Println("创建文件失败：uri=" + fullUri)
			return fmt.Errorf("创建文件失败：uri=%v", fullUri)
		}
	}
	start := time.Now()
	ole.Initialize()
	defer ole.Uninitialize()
	defer com.NewScope().Leave()
	defer func() {
		if r := recover(); r != nil {
			debug.PrintStack()
			log.Println(r)
		}
	}()
	var application *wps.Application
	var unknown *win32.IUnknown
	hResult := win32.GetActiveObject(&wps.CLSID_Application, unsafe.Pointer(&win32.IID_IUnknown), &unknown)
	if hResult != win32.S_OK {
		application, _ = wps.NewApplicationInstance(true)
		application.SetVisible(true)
	} else {
		com.AddToScope(unknown)
		var dispatch *win32.IDispatch
		hResult = unknown.QueryInterface(&wps.IID_Application_, unsafe.Pointer(&dispatch))
		if hResult != win32.S_OK {
			log.Println("接口转换失败", hResult)
		}
		com.AddToScope(dispatch)
		application = wps.NewApplication(dispatch, false, true)
		application.SetVisible(true)
	}
	workbooks := application.Workbooks()
	count := workbooks.Count()
	var workbook *wps.Workbook
	{
		for i := int32(1); i <= count; i++ {
			openFileName := workbooks.Item(i).Name()
			if strings.Contains(openFileName, fileName) {
				workbook = workbooks.Item(i)
				break
			}
		}
	}
	if workbook == nil {
		workbook = workbooks.Open(fullUri)
	}
	if workbook != nil {
		workbook.Activate()
		dispatchClass := workbook.Worksheets().Item(1)
		sheet := ole.As[*wps.Worksheet](dispatchClass)
		sheet.Activate()
		rowSize := len(rows)
		colSize := len(rows[0])
		dataArr := ole.NewArray2D[ole.Variant](int32(rowSize), int32(colSize), true)
		for row := 0; row < rowSize; row++ {
			for cell := 0; cell < colSize; cell++ {
				dataArr.SetAt2(row, cell, ole.VarScoped(rows[row][cell]))
			}
		}
		sheetRange := sheet.Range(fmt.Sprintf("A1:%v%d", cellRangeIndex[colSize], rowSize))
		sheetRange.Clear()
		sheetRange.SetValue2(dataArr)
		workbook.Save()
		//workbook.Close()
		//application.Quit()
		log.Println("【Excel】写数据完成：耗时=", time.Since(start), "，fileUri=", fullUri, "，行=", rowSize, "，列=", colSize)
	}
	return nil
}

func writeOrAppendExcel(fileUri string, data [][]interface{}) error {
	// 1. 检查文件是否存在
	_, err := os.Stat(fileUri)
	fileExists := !os.IsNotExist(err)
	var f *excelize.File
	defer f.Close()
	sheetName := "Sheet1"
	// 2. 根据文件存在性执行操作
	if fileExists {
		// 2.1 文件存在 - 打开现有文件
		f, err = excelize.OpenFile(fileUri)
		if err != nil {
			return fmt.Errorf("打开文件失败: %w", err)
		}
	} else {
		// 2.2 文件不存在 - 创建新文件
		f = excelize.NewFile()
		f.NewSheet(sheetName) // 创建默认工作表
		f.SetActiveSheet(0)   // 激活第一个工作表
	}
	// 3. 确定写入起始行（追加到现有内容之后）
	startRow := 1
	if fileExists {
		rows, _ := f.GetRows(sheetName)
		startRow = len(rows) + 1 // 计算新数据起始行
	}
	// 4. 写入数据
	for i, row := range data {
		rowNum := startRow + i
		for colIdx, cellValue := range row {
			cellName, _ := excelize.CoordinatesToCellName(colIdx+1, rowNum)
			f.SetCellValue(sheetName, cellName, cellValue)
		}
	}
	// 5. 保存文件
	if fileExists {
		if err := f.Save(); err != nil {
			return fmt.Errorf("保存失败: %w", err)
		}
	} else {
		if err := f.SaveAs(fileUri); err != nil {
			return fmt.Errorf("保存失败: %w", err)
		}
	}
	return nil
}

// NewExcelWatcher 创建新的 Excel 监听器
func NewExcelWatcher(debounceTime time.Duration) (*ExcelWatcher, error) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, fmt.Errorf("创建文件监听器失败: %w", err)
	}
	return &ExcelWatcher{
		watcher:      watcher,
		fileStates:   make(map[string]ExcelFileState),
		debounceMap:  make(map[string]*time.Timer),
		debounceTime: debounceTime,
		changeChan:   make(chan ExcelChange, 100),
	}, nil
}

// AddPath 添加要监听的路径
func (ew *ExcelWatcher) AddPath(path string) error {
	absPath, err := filepath.Abs(path)
	if err != nil {
		return fmt.Errorf("获取绝对路径失败: %w", err)
	}
	stat, err := os.Stat(absPath)
	if err != nil {
		return fmt.Errorf("路径不存在: %w", err)
	}
	ew.mu.Lock()
	defer ew.mu.Unlock()
	if stat.IsDir() {
		if err := ew.watcher.Add(absPath); err != nil {
			return fmt.Errorf("添加目录监听失败: %w", err)
		}
		ew.paths = append(ew.paths, absPath)
		// 初始化现有文件状态
		err = filepath.Walk(absPath, func(path string, info os.FileInfo, err error) error {
			if !info.IsDir() && isExcelFile(path) {
				if state, err := ew.getExcelState(path); err == nil {
					ew.fileStates[path] = state
				}
			}
			return nil
		})
	} else {
		dir := filepath.Dir(absPath)
		if err := ew.watcher.Add(dir); err != nil {
			return fmt.Errorf("添加文件目录监听失败: %w", err)
		}
		ew.paths = append(ew.paths, dir)
		log.Printf("开始监听文件: %s", absPath)

		if isExcelFile(absPath) {
			if state, err := ew.getExcelState(absPath); err == nil {
				ew.fileStates[absPath] = state
			}
		}
	}
	return nil
}

// Start 开始监听并返回变更通道
func (ew *ExcelWatcher) Start(ctx context.Context) <-chan ExcelChange {
	go func() {
		for {
			select {
			case <-ctx.Done():
				close(ew.changeChan)
				return
			case event, ok := <-ew.watcher.Events:
				if !ok {
					return
				}
				ew.handleEvent(event)
			case err, ok := <-ew.watcher.Errors:
				if !ok {
					return
				}
				log.Printf("监听错误: %v", err)
			}
		}
	}()
	return ew.changeChan
}

// handleEvent 处理文件系统事件
func (ew *ExcelWatcher) handleEvent(event fsnotify.Event) {
	if !isExcelFile(event.Name) {
		return
	}
	ew.mu.Lock()
	defer ew.mu.Unlock()
	// 只处理写操作和创建操作
	if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
		ew.debounceEvent(event.Name, func() {
			absPath, err := filepath.Abs(event.Name)
			if strings.Contains(absPath, "~$") {
				absPath = strings.Replace(absPath, "~$", "", -1)
			}
			if err != nil {
				log.Printf("获取绝对路径失败: %v", err)
				return
			}
			// 获取当前文件状态
			currentState, err := ew.getExcelState(absPath)
			if err != nil {
				log.Printf("获取当前文件状态失败: %v", err)
				return
			}
			// 获取上次保存的状态
			previousState, exists := ew.fileStates[absPath]
			// 检测变更的行
			changedRows := []int{}
			if exists {
				changedRows = ew.detectChangedRows(previousState, currentState)
			}
			changedRowData, err := ew.ReadChangedRowData(absPath, "", changedRows)
			if err != nil {
				log.Printf("读取指定行数据失败：%v", err)
				return
			}
			// 如果有变更或新文件
			if len(changedRows) > 0 || !exists {
				// 发送变更通知
				ew.changeChan <- ExcelChange{
					FilePath:       absPath,
					FileName:       filepath.Base(absPath),
					Op:             tools.If(len(changedRows) > 0, "编辑文件", "新建文件"),
					ChangedRows:    changedRows,
					ChangedRowData: changedRowData,
					//change.ChangeTime.Format("2006-01-02 15:04:05")
					ChangeTime: time.Now(),
				}
			}
			// 更新文件状态
			ew.fileStates[absPath] = currentState
		})
	}
}

// debounceEvent 防抖处理
func (ew *ExcelWatcher) debounceEvent(filePath string, f func()) {
	if strings.Contains(filePath, "~$") {
		filePath = strings.Replace(filePath, "~$", "", -1)
	}
	if timer, exists := ew.debounceMap[filePath]; exists {
		timer.Stop()
	}
	ew.debounceMap[filePath] = time.AfterFunc(ew.debounceTime, func() {
		ew.mu.Lock()
		delete(ew.debounceMap, filePath)
		ew.mu.Unlock()
		f()
	})
}

// getExcelState 获取 Excel 文件状态
func (ew *ExcelWatcher) getExcelState(filePath string) (ExcelFileState, error) {
	state := ExcelFileState{
		Sheets: make(map[string][][]string),
	}
	// 获取文件信息
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		return state, fmt.Errorf("获取文件信息失败: %w", err)
	}
	state.LastModified = fileInfo.ModTime()
	// 打开 Excel 文件
	if strings.Contains(filePath, "~$") {
		filePath = strings.Replace(filePath, "~$", "", -1)
	}
	f, err := excelize.OpenFile(filePath)
	if err != nil {
		return state, fmt.Errorf("打开Excel文件失败: %w", err)
	}
	defer f.Close()
	// 获取所有工作表
	sheets := f.GetSheetList()
	for _, sheet := range sheets {
		// 读取整个工作表
		rows, err := f.GetRows(sheet)
		if err != nil {
			log.Printf("读取工作表失败: %s, %v", sheet, err)
			continue
		}
		// 存储工作表数据
		state.Sheets[sheet] = rows
	}
	return state, nil
}

// detectChangedRows 检测变更的行
func (ew *ExcelWatcher) detectChangedRows(prevState, currState ExcelFileState) []int {
	changedRows := []int{}
	// 检测工作表变化
	for sheetName, currRows := range currState.Sheets {
		prevRows, exists := prevState.Sheets[sheetName]
		if !exists {
			// 新工作表，所有行都是新的
			for i := range currRows {
				changedRows = append(changedRows, i+1) // Excel行号从1开始
			}
			continue
		}
		// 比较行变化
		rowChanges := detectRowChanges(prevRows, currRows)
		for _, rowNum := range rowChanges {
			changedRows = append(changedRows, rowNum)
		}
	}
	// 移除重复的行号
	return removeDuplicates(changedRows)
}

func (ew *ExcelWatcher) ReadChangedRowData(fileUri, sheetName string, rowNumbers []int) ([][]string, error) {
	// 打开Excel文件
	f, err := excelize.OpenFile(fileUri)
	if err != nil {
		return nil, fmt.Errorf("打开Excel文件失败: %w", err)
	}
	defer func() {
		// 确保文件正确关闭
		if err := f.Close(); err != nil {
			fmt.Printf("关闭文件时出错: %v\n", err)
		}
	}()
	// 处理工作表名称
	if sheetName == "" {
		// 获取所有工作表的列表
		sheets := f.GetSheetList()
		if len(sheets) == 0 {
			return nil, fmt.Errorf("Excel文件中未找到任何工作表")
		}
		sheetName = sheets[0] // 使用第一个工作表
	}
	// 获取指定工作表中的所有行
	rows, err := f.GetRows(sheetName)
	if err != nil {
		return nil, fmt.Errorf("读取工作表'%s'失败: %w", sheetName, err)
	}
	// 用于存储最终结果的二维切片
	result := make([][]string, 0, len(rowNumbers))
	// 创建一个映射，以便快速检查需要哪些行
	rowMap := make(map[int]bool)
	for _, rowNum := range rowNumbers {
		rowMap[rowNum] = true
	}
	// 遍历所有行，只收集需要的行
	// 注意: rows的索引从0开始，但Excel行号从1开始
	for i, row := range rows {
		excelRowNum := i + 1 // 转换为Excel行号
		if rowMap[excelRowNum] {
			result = append(result, row)
		}
	}
	return result, nil
}

// Close 关闭监听器
func (ew *ExcelWatcher) Close() error {
	ew.mu.Lock()
	defer ew.mu.Unlock()
	for _, timer := range ew.debounceMap {
		timer.Stop()
	}
	if err := ew.watcher.Close(); err != nil {
		return err
	}
	close(ew.changeChan)
	return nil
}

// detectRowChanges 检测行变化
func detectRowChanges(prev, curr [][]string) []int {
	changes := []int{}
	// 检查现有行的变化
	minRows := len(prev)
	if len(curr) < minRows {
		minRows = len(curr)
	}
	for i := 0; i < minRows; i++ {
		if !equalRows(prev[i], curr[i]) {
			changes = append(changes, i+1) // Excel行号从1开始
		}
	}
	// 检查新增行
	for i := minRows; i < len(curr); i++ {
		changes = append(changes, i+1)
	}
	// 检查删除行（标记为变更行）
	for i := minRows; i < len(prev); i++ {
		changes = append(changes, i+1)
	}
	return changes
}

// equalRows 检查两行是否相同
func equalRows(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}

	for i, v := range a {
		if v != b[i] {
			return false
		}
	}

	return true
}

// removeDuplicates 移除重复元素
func removeDuplicates(elements []int) []int {
	encountered := map[int]bool{}
	result := []int{}

	for v := range elements {
		if !encountered[elements[v]] {
			encountered[elements[v]] = true
			result = append(result, elements[v])
		}
	}

	return result
}

// isExcelFile 检查是否为 Excel 文件
func isExcelFile(path string) bool {
	ext := strings.ToLower(filepath.Ext(path))
	return ext == ".xlsx" || ext == ".xls"
}

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		// 允许所有跨域请求，生产环境中应谨慎设置
		return true
	},
}

func Register(id string, client *websocket.Conn) {
	Clients[id] = client
}

func UnRegister(id string) {
	delete(Clients, id)
}

func Start() {
	// 1. 创建监听器，设置1秒防抖时间
	watcher, err := NewExcelWatcher(1 * time.Second)
	if err != nil {
		log.Println("创建监听器失败", err)
	}
	defer watcher.Close()
	// 2. 添加监听路径
	if err := watcher.AddPath(config.Cfg().ConfigDir); err != nil {
		log.Println("添加路径失败", err)
	}
	// 3. 启动监听器
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	changeChan := watcher.Start(ctx)
	// 4. 处理变更
	//go func() {
	//	for change := range changeChan {
	//		msg := fmt.Sprintf("=====> Excel文件变更监听：op=%v，time=%s，file=%s，rows=%v\n", tools.If(len(change.ChangedRows) > 0, "编辑", "新建"), change.ChangeTime.Format("2006-01-02 15:04:05"), change.FilePath, change.ChangedRows)
	//		log.Printf(msg)
	//	}
	//}()
	//r := gin.Default()
	//r.GET("/watch", func(c *gin.Context) {
	//	conn, _ := controller.Upgrader.Upgrade(c.Writer, c.Request, nil)
	//	go func() {
	//		ticker := time.NewTicker(3 * time.Second)
	//		for {
	//			select {
	//			case <-ticker.C:
	//				if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
	//					log.Println("心跳失败:", err)
	//				}
	//			}
	//		}
	//	}()
	for change := range changeChan {
		ChangeExcelToDB(change)
	}
	//})
	//r.Run("localhost:8081")
}

func LoadExcel(nodeType string, dataLine int) ([]models.ExcelData, error) {
	nodeType = strings.Replace(nodeType, config.Cfg().FileSuffix, "", -1)
	fileUri := config.Cfg().ConfigDir + "/" + nodeType + config.Cfg().FileSuffix
	if !tools.IsFileExists(fileUri) {
		return nil, nil
	}
	return ReadFile(fileUri, dataLine)
}
